package com.sun.lib_compress;
/*
 *
 * #                                                   #
 * #                       _oo0oo_                     #
 * #                      o8888888o                    #
 * #                      88" . "88                    #
 * #                      (| -_- |)                    #
 * #                      0\  =  /0                    #
 * #                    ___/`---'\___                  #
 * #                  .' \\|     |# '.                 #
 * #                 / \\|||  :  |||# \                #
 * #                / _||||| -:- |||||- \              #
 * #               |   | \\\  -  #/ |   |              #
 * #               | \_|  ''\---/''  |_/ |             #
 * #               \  .-\__  '-'  ___/-. /             #
 * #             ___'. .'  /--.--\  `. .'___           #
 * #          ."" '<  `.___\_<|>_/___.' >' "".         #
 * #         | | :  `- \`.;`\ _ /`;.`/ - ` : | |       #
 * #         \  \ `_.   \_ __\ /__ _/   .-` /  /       #
 * #     =====`-.____`.___ \_____/___.-`___.-'=====    #
 * #                       `=---='                     #
 * #     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~   #
 * #                                                   #
 * #               佛祖保佑         永无BUG              #
 * #                                                   #
 */

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.IntDef;
import androidx.annotation.NonNull;

import com.sun.lib_compress.provider.UriStreamProvider;
import com.sun.lib_compress.util.UriUtils;
import com.sun.lib_compress.util.Utils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * Created by ESSC07 on 2021/12/30.
 */
public class CompressUtil {
    private static final String TAG = "CompressUtil";
    private static final int NORMAL_QUALITY = 60;
    private static final long IGNORE_SIZE = 200 * 1024; // 默认低于200k的图片不压缩

    private Context context;
    private long mIgnoreSize = IGNORE_SIZE;
    private long maxFileSize = 0;
    private int min_quality = NORMAL_QUALITY; // 最小质量压缩比例
    private OnCompressListener mCompressListener;
    private Handler handler = new Handler(Looper.getMainLooper());
    private Uri mUri;

    private InputStreamProvider mInputStreamProvider;

    private CompressUtil(Context context) {
        Utils.init(context);
        this.context = context;
    }

    /**
     * 加载context，必须在load等方法之前调用
     *
     * @param context
     * @return
     */
    public static CompressUtil width(@NonNull Context context) {
        return new CompressUtil(context.getApplicationContext());
    }

    /**
     * 忽略低于指定大小的文件压缩，默认200k
     *
     * @param size 指定忽略大小，单位KB
     * @return
     */
    public CompressUtil ignoreBy(int size) {
        this.mIgnoreSize = size * 1024;
        return this;
    }

    /**
     * 设置后压缩结果 <= 指定值，默认不限制
     *
     * @param maxSize 指定最大值，单位KB
     * @return
     */
    public CompressUtil maxSize(int maxSize) {
        if (maxSize > 0) {
            this.maxFileSize = maxSize * 1024;
        }
        return this;
    }

    /**
     * 设置质量压缩最小阀值
     *
     * @param quality 范围为 0-100
     * @return
     */
    public CompressUtil minQuality(int quality) {
        if (quality >= 0 && quality <= 100)
            this.min_quality = quality;
        return this;
    }

    /**
     * 加载uri
     *
     * @param uri
     * @return
     */
    public CompressUtil load(Uri uri) {
        this.mUri = uri;
        this.mInputStreamProvider = new UriStreamProvider(context, uri);
        return this;
    }

    /**
     * 加载file
     *
     * @param file
     * @return
     */
    public CompressUtil load(File file) {
        this.mInputStreamProvider = new InputStreamProvider() {
            @Override
            public InputStream open() throws IOException {
                return new FileInputStream(file);
            }

            @Override
            public String getPath() {
                return file.getAbsolutePath();
            }
        };
        return this;
    }

    /**
     * 开始压缩
     *
     * @param listener
     */
    public void compress(@NonNull OnCompressListener listener) {
        this.mCompressListener = listener;
        AsyncTask.SERIAL_EXECUTOR.execute(() -> {
            onStart();
            if (checkMineTypeAndSize(mInputStreamProvider)) {
                try {
                    compressBySimpleSize(mInputStreamProvider);
                } catch (Exception e) {
                    e.printStackTrace();
                    onError(e.getMessage());
                }
            }
        });
    }

    private boolean checkMineTypeAndSize(InputStreamProvider provider) {
        String mineType = getMineType(provider);
        if (TextUtils.isEmpty(mineType)) {
            onError("mineType is null");
            return false;
        }
        if (mineType.lastIndexOf("/gif") != -1) {
            onError("不支持gif");
            return false;
        }
        File file = new File(provider.getPath()); //UriUtils.uri2File(context, uri);
        if (file.length() <= mIgnoreSize) {
            onSuccess(file);
            return false;
        }
        return true;
    }

    private void compressBySimpleSize(InputStreamProvider provider) throws IOException {
        InputStream stream = provider.open();// context.getContentResolver().openInputStream(uri);
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(stream, null, options);
        options.inJustDecodeBounds = false;
        int width = options.outWidth;
        int height = options.outHeight;
        options.inSampleSize = computeSize(width, height); // 设置采样率
        options.inPreferredConfig = Bitmap.Config.ARGB_8888;
        options.inPurgeable = true;
        options.inInputShareable = true;
        Bitmap bitmap = BitmapFactory.decodeStream(provider.open(), null, options);
        /*ByteArrayOutputStream baos = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.JPEG, NORMAL_QUALITY, baos);
        Log.d(TAG, "采样率值:" + options.inSampleSize + "\n采样率压缩后大小:" + (baos.toByteArray().length / 1024) + "k");
        if (limitMaxSize(baos.toByteArray().length)) {
            onSuccess(saveFile(baos));
        } else {
            compressByQuality(bitmap);
        }*/
        compressByQuality(bitmap);
    }

    // 质量压缩
    private void compressByQuality(Bitmap bitmap) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int options = NORMAL_QUALITY;
        if (maxFileSize > 0) {
            bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);
            while (baos.toByteArray().length > maxFileSize) {
                baos.reset();
                options -= 5; // 图片质量每次减5
                if (options <= min_quality)
                    options = min_quality;
                bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);
                if (options == min_quality) break;
            }
        } else {
            bitmap.compress(Bitmap.CompressFormat.JPEG, NORMAL_QUALITY, baos);
        }
        bitmap.recycle();
        if (maxFileSize > 0 && baos.toByteArray().length > maxFileSize) {
            compressBySimpleSize(new InputStreamProvider() {
                @Override
                public InputStream open() throws IOException {
                    return new ByteArrayInputStream(baos.toByteArray());
                }

                @Override
                public String getPath() {
                    return null;
                }
            });
            baos.flush();
            baos.close();
        } else {
            File file = getThumbnailFile();
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(baos.toByteArray());
            fos.flush();
            fos.close();
            baos.flush();
            baos.close();
            Log.d(TAG, "最终质量压缩值:" + options + "\n质量压缩后大小:" + (file.length() / 1024) + "k");
            onSuccess(file);
        }
    }

    // 计算采样率
    private int computeSize(int srcWidth, int srcHeight) {
        srcWidth = srcWidth % 2 == 1 ? srcWidth + 1 : srcWidth;
        srcHeight = srcHeight % 2 == 1 ? srcHeight + 1 : srcHeight;

        int longSide = Math.max(srcWidth, srcHeight);
        int shortSide = Math.min(srcWidth, srcHeight);

        float scale = ((float) shortSide / longSide);
        if (scale <= 1 && scale > 0.5625) {
            if (longSide < 1664) {
                return 1;
            } else if (longSide < 4990) {
                return 2;
            } else if (longSide > 4990 && longSide < 10240) {
                return 4;
            } else {
                return longSide / 1280 == 0 ? 1 : longSide / 1280;
            }
        } else if (scale <= 0.5625 && scale > 0.5) {
            return longSide / 1280 == 0 ? 1 : longSide / 1280;
        } else {
            return (int) Math.ceil(longSide / (1280.0 / scale));
        }
    }

    private File saveFile(ByteArrayOutputStream stream) throws IOException {
        File file = getThumbnailFile();
        FileOutputStream fos = new FileOutputStream(file);
        fos.write(stream.toByteArray());
        fos.flush();
        fos.close();
        stream.close();
        return file;
    }

    // 生成file
    private File getThumbnailFile() {
        File dirs = new File(context.getExternalFilesDir(null), "compress");
        if (!dirs.exists() || !dirs.isDirectory()) {
            dirs.mkdirs();
        }
        File file = new File(dirs, "IMG_" + System.currentTimeMillis() + ".jpg");
        if (file.exists())
            file.delete();
        return file;
    }

    private boolean limitMaxSize(long size) {
        return maxFileSize > 0 && size < maxFileSize;
    }

    private void onError(String msg) {
        handler.post(() -> {
            if (mCompressListener != null) {
                mCompressListener.onError(new RuntimeException(msg));
                mCompressListener.onComplete();
            }
        });
    }

    private void onSuccess(File file) {
        /*handler.post(() -> {
            emitter.onNext(file);
            emitter.onComplete();
        });*/
        handler.post(() -> {
            if (mCompressListener != null) {
                mCompressListener.onSuccess(file);
                mCompressListener.onComplete();
            }
        });
    }

    private void onStart() {
        handler.post(() -> {
            if (mCompressListener != null) {
                mCompressListener.onStart();
            }
        });
    }

    /**
     * 获取图片类型
     *
     * @param provider
     * @return
     */
    private static String getMineType(InputStreamProvider provider) {
        try {
            InputStream stream = provider.open();
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(stream, null, options);
            String mimeType = options.outMimeType;
            return mimeType;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}
